package cn.sunshine.ymcc.service.impl;

import cn.sunshine.ymcc.domain.PayFlow;
import cn.sunshine.ymcc.domain.PayOrder;
import cn.sunshine.ymcc.dto.AlipayNotifyDto;
import cn.sunshine.ymcc.dto.PayAliNotifyDto;
import cn.sunshine.ymcc.exception.GlobleException;
import cn.sunshine.ymcc.service.IPayFlowService;
import cn.sunshine.ymcc.service.IPayNotifyService;
import cn.sunshine.ymcc.service.IPayOrderService;
import cn.sunshine.ymcc.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.factory.Factory;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class PayNotifyServiceImpl implements IPayNotifyService {
    @Autowired
    private IPayOrderService payOrderService;
    @Autowired
    private IPayFlowService payFlowService;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /*
     * @Title: sendNotify
     * @Description: 支付宝的异步通知，也就是支付成功后的最大努力通知
     * @Author: Mr.Jiang
     * @Version: 1.0
     * @Date:  2022/8/22 10:33
     * @Parameters: [dto]
     * @Return java.lang.String
     */
    @Override
    public String sendNotify(AlipayNotifyDto alipayNotifyDto) { // 此处的参数可以加上@RequestParam 他表示 的是传过来的参数是在地址栏中以key-value的形式传递
        log.info("此处是接收到支付宝的最大努力通知");
        // 1.判断交易状态，来决定我们的业务是否需要处理
        AssertUtil.isTrue(alipayNotifyDto.isTradeSuccess(), "交易失败！");
        Map map = JSONObject.parseObject(JSON.toJSONString(alipayNotifyDto), Map.class);
        try {
            // 2.验证签名 支付宝提供了一个验证签名的接口(PayOrderServiceImpl的网页支付哪里)
            Boolean isVerify = Factory.Payment.Common().verifyNotify(map);
            if (!isVerify) {
                return "SUCCESS"; // 验证签名有问题就不用再调了
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "ERROR"; // 不清楚是什么错误，可以继续通知我
        }
        // 3.业务校验
        // 3.1检查订单
        PayOrder payOrder = payOrderService.checkPayOrder(alipayNotifyDto.getOut_trade_no());
        if (payOrder == null) {
            return "SUCCESS"; //订单不存在，就不需要再通知了
        }
        // 3.2支付宝反馈的参数（金额）和数据库订单的金额做比较
        int amountEq = new BigDecimal(alipayNotifyDto.getTotal_amount()).compareTo(payOrder.getAmount());
        if (amountEq != 0) {
            return "SUCCESS"; // 金额不对，也不需要再通知了
        }
        // 4.判断支付单的状态是否是等待状态
        boolean isWaitPay = payOrder.getPayStatus() == PayOrder.STATE_WAIT_PAY;
        if (!isWaitPay) {
            // 退款逻辑
            try {
                log.info("主动退款");
                Factory.Payment.Common().refund(payOrder.getOrderNo(), payOrder.getAmount().toString());
            } catch (Exception e) {
                e.printStackTrace();
                // 人工介入
            }
            return "SUCCESS"; // 支付单状态不对，也不通知了
        }
        // 5.上面逻辑都没有问题就修改支付单状态
        payOrder.setPayStatus(PayOrder.STATE_SUCCESS);
        Date now = new Date();
        payOrder.setUpdateTime(now);
        //payOrderService.updateById(payOrder); // 保证原子性，把它交给监听器
        // 6.记录交易流水
        PayFlow flow = new PayFlow();
        flow.setNotifyTime(now);
        flow.setSubject(payOrder.getSubject());
        flow.setOutTradeNo(alipayNotifyDto.getOut_trade_no());
        flow.setTotalAmount(new BigDecimal(alipayNotifyDto.getTotal_amount()));
        flow.setTradeStatus(alipayNotifyDto.getTrade_status());
        flow.setCode(alipayNotifyDto.getCode());
        flow.setMsg(alipayNotifyDto.getMsg());
        flow.setPassbackParams(alipayNotifyDto.getPassback_params());
        flow.setPaySuccess(alipayNotifyDto.isTradeSuccess());
        //payFlowService.insert(flow); 保存操作交给监听器

        Map<String, Object> transMap = new HashMap<>();
        transMap.put("payOrder", payOrder);
        transMap.put("flow", flow);
        //  7.发送处理支付结果的事务消息
        //  订单编号  购买人ID  课程ID
        PayAliNotifyDto payAliNotifyDto = new PayAliNotifyDto(
                payOrder.getOrderNo(),
                payOrder.getExtParams()
        );
        // 此处payAliNotifyDto不被识别，JSON.toJSONString(payAliNotifyDto))转一下
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payAliNotifyDto)).build();
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                "TxPayAliNotifyGroup", // 监听器组名
                "paynofity-topic:aliay-tags",
                message,
                transMap // 本地事务要执行的内容（就是流水和支付单状态修改）
        );

        //执行本地事务的状态
        log.info("发送事务消息开始");
        LocalTransactionState localTransactionState = sendResult.getLocalTransactionState();
        //发送消息状态
        SendStatus sendStatus = sendResult.getSendStatus();
        log.info("发送事务消息完成");

        if (localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus != SendStatus.SEND_OK) {
            throw new GlobleException("500","处理支付结果失败！请重给我推消息");
        }
        return "SUCCESS";
    }
    /*
    * @Title: updatePayOrderAndInsertFlow
    * @Description: 执行本地事务
    * @Author: Mr.Jiang
    * @Version: 1.0
    * @Date:  2022/8/22 18:46
    * @Parameters: [map]
    * @Return void
    */
    @Override
    public void updatePayOrderAndInsertFlow(Map<String, Object> map) {
        PayOrder payOrder = (PayOrder) map.get("payOrder");
        PayFlow flow = (PayFlow) map.get("flow");
        PayOrder payOrderTmp = payOrderService.selectById(payOrder.getId());
        boolean isWaitePay = payOrderTmp.getPayStatus() == PayOrder.STATE_WAIT_PAY;
        AssertUtil.isTrue(isWaitePay, "状态异常！");
        payOrderService.updateById(payOrder);
        payFlowService.insert(flow);
    }
}
